#include <cstring>
#include <cctype>
#include <iostream>
#include <stdexcept>

#include "SmartString.h"
#include "SmartArray.h"

//SmartString::SplitPoint::NoSplit.
SmartString::SplitPoint::NoSplit::NoSplit(unsigned int len):mLen(len){}
SmartString::SplitPoint::NoSplit::operator unsigned int()
{
	return mLen;
}
//SmartString::SplitPoint::Split.
SmartString::SplitPoint::Split::Split(unsigned int len,char* ptr):SmartString::SplitPoint::NoSplit(len)
{
	newString = new SmartString(ptr,mLen);
}
SmartString* SmartString::SplitPoint::Split::operator () ()
{
	return newString;
}
SmartString::SplitPoint::Split::~Split()
{
	if ( --(newString->_count)==0 )
		delete newString;
}
//SmartString::SplitPoint.
SmartString::SplitPoint::SplitPoint():mPoints(new SmartString("")),mKeys(){}
SmartString::SplitPoint& SmartString::SplitPoint::AddPoint( const char* src)
{
	mKeys.Append(new SmartString( src) );
	return *this;
}
SmartString::SplitPoint& SmartString::SplitPoint::AddPoint( const char src )
{
	mPoints().Append( src );
	return *this;
}
SmartString::SplitPoint::~SplitPoint()
{
}
unsigned int SmartString::SplitPoint::operator()(char* ptr) const
{
	if(strchr(mPoints(),*ptr))
	{
			char* o = ptr;
			while(strchr(mPoints(),*ptr)) ptr++;
			std::cout << "Not including : '" << SmartString(o,((unsigned int)(ptr-o))) << "'" << std::endl;
			throw SplitPoint::NoSplit(((unsigned int)(ptr-o)));
	}
	else
	{
			char* o = ptr;
			while(strchr(mPoints(),*ptr)==NULL) ptr++;
			std::cout << "Including : '" << SmartString(o,((unsigned int)(ptr-o))) << "'" << std::endl;
			throw SplitPoint::Split(((unsigned int)(ptr-o)),o);
	}
	return 1;
}

//SmartString.
unsigned int SmartString::gBufferSize = 10;

void SmartString::Allocate( unsigned int len ) throw (Iterable::UnneededAllocation)
{
	char* nmStr = static_cast<char*>(memcpy( new char[ mAlloc = newSize(len) ], mStr, mUsed ));
	delete mStr;
	mStr = nmStr;
}

SmartString SmartString::operator [] ( const Iterable::Slice& aSlice ){
	try{
		unsigned int lStart = aSlice.start(mUsed), lEnd = aSlice.end(mUsed);
		return SmartString(mStr + lStart,lEnd - lStart);
	} catch ( std::range_error ){
		return SmartString("Invalid slice was provided.");
	}
}

int SmartString::operator [] ( char search )
{
	return ((int)strchr( mStr, search))-((int)search);
}

char SmartString::operator [] ( int aIndice){
	return mStr[SmartString::Slice(0,mUsed).start(aIndice)];
}

SmartString::operator const char *() const
{
	return mStr;
}

SmartString& SmartString::Empty()
{
	delete[] mStr;
	mStr = new char[mAlloc = mUsed = 1];
	mStr[0] = '\0';
	return *this;
}

const SmartArray<SmartString> SmartString::Explode( const SmartString::SplitPoint& sp )
{
	SmartArray<SmartString> ret;
	for( char* ptr=mStr,*end=mStr+mUsed; ptr<end; ptr++)
	{
		try{ sp(ptr); }
		catch( SmartString::SplitPoint::Split& lSplit )
		{
			ret.Append(lSplit());
			ptr += static_cast<unsigned int>(lSplit);
		}
		catch( SmartString::SplitPoint::NoSplit& noSplit )
		{
			ptr += static_cast<unsigned int>(noSplit);
		}
	}
	return ret;
}

SmartString& SmartString::Append(const char* src)
{
	return Append(src,strlen(src));
}

SmartString& SmartString::Append(const char* src, unsigned int aLen)
{
	try{
		Allocate(aLen);
	} catch ( SmartString::UnneededAllocation ){}
	memcpy(mStr+mUsed-1,src,aLen);
	mUsed += aLen;
	mStr[mUsed-1] = '\0';
	return *this;
}

SmartString& SmartString::Append( const char src )
{
	return Append( &src, 1 );
}

SmartString& SmartString::Insert(const char* src, int aWhere )
{
	return Insert(src,strlen(src),aWhere);
}

SmartString& SmartString::Insert(const char* src, unsigned int len, int aWhere)
{
	try{ Allocate(len); }
	catch( Iterable::UnneededAllocation ){}
	aWhere = Iterable::Slice(0,aWhere).end(mUsed);
	char* temp = static_cast<char*>(memcpy(new char[mUsed-aWhere],mStr + aWhere,mUsed-aWhere));
	memcpy(mStr + aWhere,src,len);
	memcpy(mStr + aWhere + len,temp,mUsed - aWhere);
	mUsed += len;
	delete [] temp;
	return *this;
}

SmartString& SmartString::Prepend( const char* src )
{
	return Prepend( src, strlen(src) );
}

SmartString& SmartString::Prepend( const char* src, unsigned int len )
{
	SmartString temp(src);
	temp.Append(mStr);
	char* stemp = mStr;
	mStr = temp.mStr;
	temp.mStr = stemp;
	mUsed = temp.mUsed;
	mAlloc = temp.mAlloc;
	return *this;
}

SmartString::SmartString( const SmartString& src):mStr(NULL)
{
	Empty().Append(src.mStr);
}

SmartString::SmartString(const char* src):mStr(NULL)
{
	Empty().Append(src);
}

SmartString::SmartString(const char* src,unsigned int aLen):mStr(NULL)
{
	Empty().Append(src,aLen);
}

SmartString::SmartString(void):mStr(NULL)
{
	Empty();
}

SmartString::~SmartString()
{
	delete [] mStr;
}

int SmartString::Similar ( const char* src )
{
	char* ptr = mStr;
	do
	{
		while(isspace(*ptr))ptr++;
		while(isspace(*src))src++;
		if( toupper(*ptr) ^ toupper(*src) )
			return -1 * ( ptr - mStr );
	} while(*++ptr&&*++src);
	return 1;
}

unsigned int SmartString::Contains( const char* src, bool aSingle ) const
{
	unsigned int ret = 0;
	//if(aSingle){
	for(unsigned int i = 0; i<mUsed; i++){
		for( char* ptr = ((char*)src);*ptr;ptr++){
			if( toupper(*ptr) ^ toupper(mStr[i]) ) continue;
			ret++;
			break;
		}
	}
	//} else if{

	//}
	return ret;
}

bool SmartString::operator==( const char* src ) const
{
	for( char* ptr = mStr; *ptr&&*src; ptr++,src++ )
		if(*ptr^*src) return false;
	return true;
}

void SmartString::LoadFromFile( std::istream& input )
{
	Empty();
	unsigned int len;
	input.read((char*)&len,4);
	Allocate(len);
	input.read(mStr,mUsed=len);
}

void SmartString::ExportToFile( std::ostream& output )
{
	output.write( (char*)&mUsed, 4);
	output.write( mStr, mUsed);
}